﻿using FictionsDownload.BLL.CefSharp;
using FictionsDownload.Enums;
using FictionsDownload.Events;
using Zzw;
using FictionsDownload.Model;
using FictionsDownload.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using FictionsDownload.Helper;

namespace FictionsDownload.BLL
{
    public class CefSharpQueue
    {
        /// <summary>
        /// 有新的消息需要添加（事件声明）
        /// </summary>
        public event AddMessageEventHandler AddMessage;
        public event EventHandler LvBookcaseRefresh;
        /// <summary>
        /// Cef浏览器窗口
        /// </summary>
        public ICefSharp webBrowser { get; set; }
        private Folder folder = new Folder();
        private WebFormat webFormat = new WebFormat();
        private Book book = new Book();
        /// <summary>
        /// 构造函数，必传参数
        /// </summary>
        /// <param name="formCefSharp"></param>
        public CefSharpQueue(ICefSharp webBrowser)
        {
            this.webBrowser = webBrowser;
            LoopProcs();
        }
        ///声明队列和执行查询
        public Queue<Action> Procs = new Queue<Action>();
        /// <summary>
        /// 添加一个任务
        /// </summary>
        /// <param name="proc"></param>
        public void Register(Action proc)
        {
            Procs.Enqueue(proc);
        }
        public async void LoopProcs()
        {
            while (true)
            {
                if (Procs.Count > 0)
                {
                    var handler = Procs.Dequeue();
                    handler.DynamicInvoke();
                    return;
                }
                else
                {
                    await Task.Delay(1000);
                }
            }
        }
        /// <summary>
        /// 等待浏览器初始化
        /// </summary>
        public void IsBrowserInit()
        {
            this.Register(async () =>
            {
                this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "开始运行IsBrowserInit()"));
                for (int i = 0; !this.webBrowser.IsBrowserInitialized; i++)
                {
                    if (i > 20)
                    {
                        this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "浏览器未初始化，已经等待20秒，中断"));
                        LoopProcs();
                        return;
                    }
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "浏览器未初始化，等待1秒，次数:" + i));
                    await Task.Delay(1000);
                }
                this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "浏览器初始化完成"));
                LoopProcs();
                return;
            });
        }
        /// <summary>
        /// 判断是否登陆
        /// </summary>
        public void IsLogin()
        {
            this.Register(async () => {
                this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "开始检查登陆状态"));
                if (!await webBrowser.LoginNameAsync())
                {
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "帐号未登录，尝试登录"));
                    await this.webBrowser.LoginAccount();
                }
                else
                {
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "帐号已登录"));
                }
                LoopProcs();
                return;
            });
        }
        /// <summary>
        /// 加载某个网址
        /// </summary>
        /// <param name="url"></param>
        public void LoadUrl(string url)
        {
            this.Register(async () =>
            {
                this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "开始进入Url=" + url));
                if ((await webBrowser.LoadUrlAsync(url)).IsNullOrEmpty())
                {
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "进入Url=" + url+ "失败，中断"));
                }
                else
                {
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "进入Url=" + url + "成功"));
                }
                LoopProcs();
                return;
            });
        }

        /// <summary>
        /// 刷新书架信息（全流程）
        /// </summary>
        public void RefreshBookshelf()
        {
            this.LoadUrl("https://my.qidian.com/bookcase");
            this.Register(async () =>
            {
                await this.LoadBooksAsync(this.webBrowser.Html);
                this.LoopProcs();
                return;
            });
        }
        /// <summary>
        /// 读取书架书籍写入数据库
        /// </summary>
        public async Task LoadBooksAsync(string html)
        {
            Dictionary<string, string> bookcases = QidianHelper.GetBookcase(html);
            bookcases.Add("", "默认分组");
            this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "书架表读取成功,将书架写入数据库"));
            var folders = BookCaseToDB(bookcases);
            LvBookcaseRefresh(bookcases,null);
            this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "书架表读取成功,读取书架内的书籍"));
            foreach (var folder in folders)
            {
                Dictionary<string, string> books = await this.GetBooksAsync(folder.Remarks);
                if (books == null)
                {
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "读取书架:" + folder.Name + "时返回空值，中断"));
                    return;
                }
                else
                {
                    this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "读取书架:" + folder.Name + "的内容读取成功，共计:" + books.Count + "本,写入数据库"));
                    BooksToDB(books, folder.Id);
                    LvBookcaseRefresh(books, null);
                }
            }
            this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "书架内的书籍全部读取完毕，进行下一步操作"));
        }

        /// <summary>
        /// 将某个书架的书籍加入数据库
        /// </summary>
        /// <param name="keyValuePairs"></param>
        private void BooksToDB(Dictionary<string, string> keyValuePairs, string folderId)
        {
            //查找书籍是否存在
            WebFormatModel webFormatModel = webFormat.FindByUrlDns("https://book.qidian.com");
            foreach (var item in keyValuePairs)
            {
                BookModel bookModel = new BookModel
                {
                    Name = item.Value,
                    Url = "https:" + item.Key,
                    DirectoryUrl = "https:" + item.Key + "#Catalog",
                    FolderId = folderId,
                };
                if (webFormatModel != null)
                {
                    bookModel.HtmlHead = webFormatModel.HtmlHead;
                    bookModel.HtmlTail = webFormatModel.HtmlTail;
                    bookModel.VolumeHead = webFormatModel.VolumeHead;
                    bookModel.VolumeTail = webFormatModel.VolumeTail;
                    bookModel.TextHead = webFormatModel.TextHead;
                    bookModel.TextTail = webFormatModel.TextTail;
                    bookModel.SortOrder = webFormatModel.SortOrder;
                }
                if (!book.Add(bookModel))
                {
                    if (book.Message == "已有URL")
                    {
                        BookModel tempBook = book.Find<BookModel>(b => b.Url == bookModel.Url).First();
                        if (tempBook.FolderId != bookModel.FolderId)
                        {
                            tempBook.FolderId = bookModel.FolderId;
                            if (!book.Update(tempBook))
                            {
                                this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "修改书籍: " + bookModel.Name + "的分组失败: " + book.Message));
                            }
                        }
                    }
                    else if (book.Message == "已有名称")
                    {
                        bookModel.Name += "起点";
                        if (!book.Add(bookModel))
                        {
                            this.AddMessage?.Invoke(this, new AddMessageEventArgs(MessageTypeEnum.添加, "添加书籍:" + bookModel.Name + "失败:" + book.Message));
                        }
                    }
                }
            }
            book.SaveChanges();
        }
        /// <summary>
        /// 将书架的键值表写入数据库的文件夹表
        /// </summary>
        /// <param name="bookcases"></param>
        private List<FolderModel> BookCaseToDB(Dictionary<string, string> bookcases)
        {
            //所有的文件夹，都在一个“起点”文件夹下，所以“起点”文件夹必须存在。
            var qidianFolder = folder.Get<FolderModel>(f => f.Name == "起点");
            var tempFolders = folder.GetList<FolderModel>(m => m.FolderId == FolderModel.BaseNode);
            int sortIndex = tempFolders.Any() ? tempFolders.Max(m => m.SortIndex) : 0;
            if (qidianFolder == null)
            {
                FolderModel tempFolder = new FolderModel
                {
                    Name = "起点",
                    SortIndex = ++sortIndex,
                };
                if (folder.Add(tempFolder))
                {
                    folder.SaveChanges();
                    //起点书架添加成功，再回调一次处理
                    return BookCaseToDB(bookcases);
                }
                else
                {
                    return null;
                }
            }
            //找到了起点文件夹的Id，开始添加或者修改分组文件夹。
            foreach (var bookcase in bookcases)
            {
                //查找书架Id和书架名都满足的，不做修改
                var folders = folder.Find<FolderModel>(f => f.Name == bookcase.Value).Where(f => f.Remarks == bookcase.Key);
                if (!folders.Any())
                {
                    //没有相同书架名的，就寻找相同书架Id的，做改名处理
                    folders = folder.Find<FolderModel>(f => f.Remarks == bookcase.Key);
                    if (!folders.Any())
                    {   //书架名和书架Id都找不到的，做新增处理
                        folder.Insert(new FolderModel
                        {
                            Name = bookcase.Value,
                            Remarks = bookcase.Key,
                            FolderId = qidianFolder.Id,
                            SortIndex = ++sortIndex,
                        });
                    }
                    else
                    {
                        var tempFolder = folders.First();
                        tempFolder.Name = bookcase.Value;
                        folder.Update(tempFolder);
                    }
                }
            }
            folder.SaveChanges();
            return folder.GetList<FolderModel>(f => f.FolderId == qidianFolder.Id);
        }

        /// <summary>
        /// 传入书架Id，获取当前书架下的书籍（只能获取第一页也就是前100本的书籍）
        /// </summary>
        /// <param name="bookcaseId"></param>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetBooksAsync(string bookcaseId)
        {
            string bookcaseUrl = "https://my.qidian.com/bookcase/" + bookcaseId;
            string html = await webBrowser.LoadUrlAsync(bookcaseUrl);
            if (html.IsNoNullOrEmpty())
            {
                string bodyHtml = HtmlHelper.HtmlTrim(html, "<th scope=\"col\" width=\"20\">", "<script id=\"tplGameRec\"");
                //提取下载链接
                Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
                Regex reg = new Regex("<a href=\"(//book.qidian.com/info/.*?)\" target=\"_blank\" title=\"(.*?)\"");
                while (true)
                {
                    Match match = reg.Match(bodyHtml);
                    if (match.Success)
                    {
                        if (!keyValuePairs.ContainsKey(match.Groups[1].Value))
                        {
                            keyValuePairs.Add(match.Groups[1].Value, match.Groups[2].Value);
                        }
                    }
                    else
                    {
                        break;
                    }
                    bodyHtml = bodyHtml.Substring(match.Index + match.Length);
                }
                keyValuePairs = keyValuePairs.OrderBy(m => m.Key).ToDictionary(k => k.Key, v => v.Value);
                return keyValuePairs;
            }
            return new Dictionary<string, string>();
        }
    }
}
